package com.ehotting.mqbd.zw.netty.netty;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.ehotting.eaf.core.StringHelper;
import com.ehotting.eaf.core.snow.SnowflakeUtil;
import com.ehotting.eaf.core.utils.StringUtils;
import com.ehotting.eaf.netty.action.Action;
import com.ehotting.eaf.netty.action.ActionAble;
import com.ehotting.eaf.netty.client.TcpClient;
import com.ehotting.eaf.netty.constant.NettyState;
import com.ehotting.eaf.netty.domain.DataAble;
import com.ehotting.eaf.netty.domain.Response;
import com.ehotting.eaf.netty.loggers.NettyMsgReceiveLogger;
import com.ehotting.eaf.netty.sync.SyncFutureMapHelper;
import com.ehotting.edsta.mqbd.api.enums.SysModuleEnum;
import com.ehotting.mqbd.core.logger.LOGGER_KEY;
import com.ehotting.mqbd.core.manager.AgentLinkManager;
import com.ehotting.mqbd.zw.api.constants.ActionConstants;
import com.ehotting.mqbd.zw.api.domain.netty.ZwCommPacket;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;

@Sharable
public class ZwNettyHandler extends SimpleChannelInboundHandler<ZwCommPacket> {

    protected final Logger logger = LoggerFactory.getLogger(this.getClass());

    protected ZwNettyActionFactory zwNettyActionFactory;

    protected TcpClient tcpClient;

    protected NettyMsgReceiveLogger nettyMsgReceiveLogger;

    protected SyncFutureMapHelper<ZwCommPacket> zwNettySyncFutureMapHelper;

    private AgentLinkManager agentLinkManager;

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, ZwCommPacket request) {
        MDC.put(LOGGER_KEY.MODULE,SysModuleEnum.SIMULATION.getCode());
        MDC.put(LOGGER_KEY.TRACE_ID, SnowflakeUtil.nextId64());
        String msg = JSON.toJSONString(request, SerializerFeature.DisableCircularReferenceDetect);
        if (request.getHead().getCmd()!= ActionConstants.Cmd_FileSend.getZwCommandVal()){
            // 文件发送，不打印
            nettyMsgReceiveLogger.info(msg);
        }
        DataAble response = null;
        String serial = null;
        try {
            /**处理业务**/
            String action = request.getAction();
            if (StringUtils.isBlank(action)) {
                logger.error("action is null,data is {},return.", msg);
                return;
            }

            ActionAble<ZwCommPacket> nettyAction = zwNettyActionFactory.getAction(action);
            if (nettyAction == null) {
                logger.error("nettyAction[{},action={}] is null,return.", ctx.channel().remoteAddress(), action);
                return;
            }

            Action actionType = nettyAction.getClass().getAnnotation(Action.class);
            String key = request.getHead().getCmd()+"-"+request.getHead().getPeerPackId() + "";
            if (zwNettySyncFutureMapHelper != null && (actionType == null || actionType.isNotice())
                    && StringUtils.hasText(key)) {
//                logger.info("{} putAndNotify",actionType);
                zwNettySyncFutureMapHelper.putAndNotify(key, request);
            }

            ZwCommPacket req = (ZwCommPacket) JSON.parseObject(JSON.toJSONString(request),nettyAction.getRequestClass());

            setBackInfo(ctx,  req);

            response = nettyAction.invoke(ctx, req);
        } catch (Exception e) {
            logger.error("data:{}", msg);
            logger.error("nettyAction invoke error", e);
            response = new Response(NettyState.ACTION_EXCETION, NettyState.FAIL, serial, e.getMessage());
        } finally {
            /**返回数据**/
            if (response != null) {
                String result = JSON.toJSONString(response, SerializerFeature.DisableCircularReferenceDetect);
                logger.info("result:{}", result);
                ctx.writeAndFlush(result);
            }
            MDC.clear();
        }

    }

    private void setBackInfo(ChannelHandlerContext ctx, ZwCommPacket req){
        String channelId = ctx.channel().id().asLongText();
        String agentId = agentLinkManager.getAgentIdByChannelId(SysModuleEnum.SIMULATION.getCode(),channelId);
        if(agentId==null){
            logger.error("Get agentId[channelId={}] is null,channelId");
            agentLinkManager.deleteAgentIdByChannelId(SysModuleEnum.SIMULATION.getCode(),channelId);
            tcpClient.shutdown();
            return;
        }
        req.setUserId(agentLinkManager.getUserId(agentId));
        req.setFrom(agentId);

        String clientIp = agentLinkManager.getClientIpByAgentId(agentId);
        if(StringHelper.isBlank(clientIp)){
            logger.error("Get clientIp[agentId={}] is null,close tcp.",agentId);
            agentLinkManager.deleteAgentIdByChannelId(SysModuleEnum.SIMULATION.getCode(),channelId);
            tcpClient.shutdown();
            return;
        }
        req.setDevice(clientIp);
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) {
        logger.info(ctx.channel().remoteAddress() + "   ----Active");
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        logger.info("Unexpected exception from downstream." + cause);
        cause.printStackTrace();
        ctx.close().addListener(ChannelFutureListener.CLOSE);
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        super.channelInactive(ctx);
        if(tcpClient!=null){
            tcpClient.doConnect();
        }
    }

    public void setZwNettyActionFactory(ZwNettyActionFactory zwNettyActionFactory) {
        this.zwNettyActionFactory = zwNettyActionFactory;
    }

    public void setTcpClient(TcpClient tcpClient) {
        this.tcpClient = tcpClient;
    }

    public void setNettyMsgReceiveLogger(NettyMsgReceiveLogger nettyMsgReceiveLogger) {
        this.nettyMsgReceiveLogger = nettyMsgReceiveLogger;
    }

    public void setZwNettySyncFutureMapHelper(SyncFutureMapHelper<ZwCommPacket> zwNettySyncFutureMapHelper) {
        this.zwNettySyncFutureMapHelper = zwNettySyncFutureMapHelper;
    }

    public void setAgentLinkManager(AgentLinkManager agentLinkManager) {
        this.agentLinkManager = agentLinkManager;
    }
}